/***
 * 	Copyright (c) 2012 yanghui
 * 	Author: yanghui1986527@gmail.com
 *  http://code.google.com/p/gravatar4android/
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.snowdream.gravatar4android;

import org.apache.commons.validator.routines.EmailValidator;
import org.apache.commons.validator.routines.UrlValidator;

import android.text.TextUtils;

import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
import com.snowdream.gravatar4android.data.GravatarProfile;
import com.snowdream.gravatar4android.data.GravatarReponse;
import com.snowdream.gravatar4android.exception.GravatarException;
import com.snowdream.gravatar4android.listener.AbstractGravatarListener;
import com.snowdream.gravatar4android.util.EncryptUtil;

/**
 * A gravatar is a dynamic image resource that is requested from the
 * gravatar.com server. This class calculates the gravatar url and fetches
 * gravatar images. See http://en.gravatar.com/site/implement/url .
 * 
 * This class is thread-safe, Gravatar objects can be shared.
 * 
 * Usage example:
 * <pre>
 * //Get the gravatar instance
 * Gravatar gravatar = Gravatar.getInstance();
 * 
 * //set the email address
 * try {
 *     gravatar.setEmail("beau@dentedreality.com.au");
 * } catch (GravatarException e1) {
 *     e1.printStackTrace();
 * }
 * 
 * //use https:// protocol
 * gravatar.setSecure(true);
 *
 * //get the gravatar url
 * myImage.setImageUrl(gravatar.getGravatarUrl());
 * 
 * //get the profile asynchronous
 * gravatar.asyncGetProfile(new AbstractGravatarListener<GravatarProfile>() {
 * 
 *     @Override
 *     public void onStart() {
 *         // TODO Auto-generated method stub
 * 
 *     }
 * 
 *     @Override
 *     public void onGravatarException(GravatarException e) {
 *         // TODO Auto-generated method stub
 * 
 *     }
 * 
 *     @Override
 *     public void onSuccess(GravatarProfile obj) {
 *         // TODO Auto-generated method stub
 *
 *    }
 *});
 * </pre>
 */
public class Gravatar {

    protected static final String TAG = "Gravatar";

    /**
     * The instance of the Gravtar
     */
    private static Gravatar mGravatar = null;

    private String mEmail = null;
    private String mEmailHash = "205e460b479e2e5b48aec07710c08d50";
    private int mSize = GravatarSize.DEFAULT;
    private boolean misUseSSL = false;

    private int misForceDefault = GravatarForceDefault.FALSE;

    private String mRating = GravatarRating.G;

    private String mdefaultImage = GravatarDefaultImage.GRAVATAR_ICON;

    private String mType = GravatarType.DEFAULT;

    private static AsyncHttpClient client = new AsyncHttpClient();
    String[] allowedContentTypes = new String[] { "image/png", "image/jpeg" };

    private Gson mgson = null;

    /**
     * construct the gravatar instance
     */
    private Gravatar () {
        mgson = new Gson();
    }

    /**
     * get the Gravatar instance
     * 
     * @return the Gravatar instance
     */
    public synchronized static Gravatar getInstance() {
        if (mGravatar == null) {
            mGravatar = new Gravatar();
        }
        return mGravatar;
    }

    /**
     * set the email address, and calculate the hash value for it.
     * 
     * @param email  the email address
     * @throws GravatarException
     */
    public void setEmail(String email) throws GravatarException{
        mEmailHash = "";
        if(!EmailValidator.getInstance().isValid(email.trim())){
            throw new GravatarException(GravatarException.EMAIL_INVAILD);
        }else{
            mEmailHash = EncryptUtil.getInstance().md5Hex(email);
        }
    }

    /**
     * The email address of the gravatar
     * 
     * @return The email address of the gravatar
     */
    public String getEmail(){
        return this.mEmail;
    }

    /**
     * The hash value for the email
     * 
     * @return The hash value for the email
     */
    public String getEmailHash(){
        return this.mEmailHash;
    }

    /**
     * Specify a gravatar size between 1 and 512 pixels. If you omit this, a
     * default size of 80 pixels is used.
     * 
     * @param sizeInPixels  The Gravatar size ,between 1 and 512 pixels.
     */
    public void setSize(int sizeInPixels) throws IllegalArgumentException{
        if(sizeInPixels >= GravatarSize.MIN && sizeInPixels <= GravatarSize.MAX){
            this.mSize = sizeInPixels;
        }else{
            throw new IllegalArgumentException("The size is invalid. please make sure that the size is between 1px to 512px.");
        }
    }

    /**
     * get the size of the gravatar 
     * 
     * @return the size of the gravatar
     */
    public int getSize(){
        return this.mSize;
    }

    /**
     * specify whether to use the default gravatar always.
     * 
     * @param isForceDefault  whether to use the default gravatar always.
     */
    public void setForceDefault(int isForceDefault){
        misForceDefault = isForceDefault;
    }

    /**
     * whether to use the default gravatar always.
     * 
     * @return whether to use the default gravatar always.
     */
    public int isForceDefault(){
        return misForceDefault;
    }

    /**
     * specify whether use https://  to get the gravatar and the profile 
     * 
     * @param isSecure whether use https://
     */
    public void setSecure(boolean isSecure){
        this.misUseSSL = isSecure;
    }

    /**
     * whether use https://  to get the gravatar and the profile 
     * 
     * @return whether use https://
     */
    public boolean isSecure(){
        return this.misUseSSL;
    }

    /**
     * specify a rating to ban gravatar images with explicit content.
     */
    public void setRating(String rating) throws IllegalArgumentException{
        if(rating.equals(GravatarRating.G)||
                rating.equals(GravatarRating.PG)||                
                rating.equals(GravatarRating.R)||                
                rating.equals(GravatarRating.X)){
            this.mRating = rating;
        }else{
            throw new IllegalArgumentException("The Rating is invalid.please check it");
        }
    }

    /**
     * get the Rating of the Gravatar
     * 
     * @return the Rating of the Gravatar
     */
    public String getRating(){
        return this.mRating;
    }

    /**
     * specify the default image to be produced if no gravatar image was found.
     */
    public void setDefaultImage(String defaultImage) throws IllegalArgumentException{
        if(defaultImage.equals(GravatarDefaultImage.GRAVATAR_ICON)||
                defaultImage.equals(GravatarDefaultImage.HTTP_404)||
                defaultImage.equals(GravatarDefaultImage.IDENTICON)||
                defaultImage.equals(GravatarDefaultImage.MM)||
                defaultImage.equals(GravatarDefaultImage.MONSTERID)||
                defaultImage.equals(GravatarDefaultImage.RETRO)||
                defaultImage.equals(GravatarDefaultImage.WAVATAR)||
                UrlValidator.getInstance().isValid(defaultImage)){
            this.mdefaultImage = defaultImage;
        }else{
            throw new IllegalArgumentException("The default image is invaild.");
        }

    }

    /**
     * get the Gravatar URL.
     */
    public String getGravatarUrl() {
        return getGravatarUrl(GravatarType.DEFAULT);
    }

    /**
     * get the Gravatar URL for the given email address.
     */
    public String getGravatarUrl(String type) {
        this.mType = type;

        StringBuilder builder = new StringBuilder();
        if(misUseSSL){
            builder.append(Uris.BASE_SECURE);
        }else{
            builder.append(Uris.BASE);
        }

        builder.append(mEmailHash);

        if(!TextUtils.isEmpty(mType)){
            builder.append(".");
            builder.append(mType);
        }

        builder.append("?");
        builder.append("s=");
        builder.append(mSize);
        builder.append("&");
        builder.append("r=");
        builder.append(mRating);     

        if(!TextUtils.isEmpty(mdefaultImage)){
            builder.append("&");
            builder.append("d=");
            builder.append(mdefaultImage); 
        }

        if(1 == misForceDefault){
            builder.append("&");
            builder.append("f=y");
        }

        return builder.toString();
    }

    /**
     * get the profile url
     * 
     * @return the profile url
     */
    private String getProfileUrl() {
        StringBuilder builder = new StringBuilder();
        if(misUseSSL){
            builder.append(Uris.DOMAIN_SECURE);
        }else{
            builder.append(Uris.DOMAIN);
        }

        builder.append(mEmailHash);
        builder.append(".json");

        return builder.toString();
    }

    @SuppressWarnings("unused")
    private void asyncGetGavatar(String email) {
        // client.get(getImageUrl(),
    }

    /**
     * get the profile asynchronous.
     * 
     * @param getProfileListener the action listener
     */
    public void asyncGetProfile(final AbstractGravatarListener<GravatarProfile> getProfileListener){
        asyncGetProfile(mEmail,getProfileListener);
    }

    /**
     * get the profile for the email asynchronous.
     * 
     * @param email the email address of the user
     * @param getProfileListener the action listener
     */
    public void asyncGetProfile(String email,final AbstractGravatarListener<GravatarProfile> getProfileListener){
        try {
            setEmail(email);
        } catch (GravatarException e) {
            e.printStackTrace();
            getProfileListener.onGravatarException(e);
        }

        client.get(getProfileUrl(),new AsyncHttpResponseHandler(){

            @Override
            public void onFailure(Throwable arg0, String arg1) {
                super.onFailure(arg0, arg1);
                getProfileListener.onGravatarException(new GravatarException(GravatarException.GET_GRAVATAR_PROFILE_FAILED,arg0.getMessage()));
            }

            @Override
            public void onFinish() {
                super.onFinish();
            }

            @Override
            public void onStart() {
                super.onStart();
                getProfileListener.onStart();
            }

            @Override
            public void onSuccess(String arg0) {
                super.onSuccess(arg0);
                try {
                    getProfileListener.onSuccess(getGravatarProfile(arg0));
                } catch (JsonSyntaxException e) {
                    getProfileListener.onGravatarException(new GravatarException(GravatarException.GET_GRAVATAR_PROFILE_FAILED));
                }
            }
        });
    }

    /**
     * Parse the json string
     * 
     * @param reponse the json string
     * @return GravatarProfile
     * @throws JsonSyntaxException
     */
    private GravatarProfile getGravatarProfile(String reponse) throws JsonSyntaxException{

        GravatarReponse gravatarReponse = mgson.fromJson(reponse,GravatarReponse.class);

        return gravatarReponse.getEntry().get(0);
    }
}
